package com.zzvcom.office.services;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.onlyoffice.manager.request.RequestManager;
import com.onlyoffice.model.convertservice.ConvertRequest;
import com.onlyoffice.model.convertservice.ConvertResponse;
import com.onlyoffice.service.convert.ConvertService;
import com.zzvcom.office.cfg.AppConfig;
import com.zzvcom.office.documentserver.storage.FileStorageMutator;
import com.zzvcom.office.documentserver.storage.FileStoragePathBuilder;
import com.zzvcom.office.dto.FormatsList;
import com.zzvcom.office.entities.OfficeFile;
import com.zzvcom.office.entities.OfficePdf;
import com.zzvcom.office.exception.OfficeConverterRunningException;
import com.zzvcom.office.exception.OfficeException;
import com.zzvcom.office.sdk.manager.DocumentManager;
import com.zzvcom.office.util.CommonUtil;
import com.zzvcom.office.util.LogSdkItemUtil;
import com.zzvcom.office.util.MyFileUtil;
import com.zzvcom.office.util.UserMetadataUtil;
import com.zzvcom.office.vo.FilePdfVo;
import com.zzvcom.office.vo.OfficeFileResultVo;
import com.zzvcom.util.FileItem;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.io.File;
import java.net.MalformedURLException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class DownloadServices {

    @Autowired
    private AppConfig appConfig;

    @Autowired
    private LogSdkItemUtil logSdkItemUtil;



    // 获取所有支持的转换格式
    private Set<String> convertExtensions = Collections.unmodifiableSet(CollUtil.newHashSet("doc", "ppt", "xls"));
    /**
     * 所有可以直接使用的格式
     */
    private Set<String> enableExtensions = Collections.unmodifiableSet(CollUtil.newHashSet("docx", "pptx", "xslx", "pdf"));

    private Set<String> allowExtensions = Collections.unmodifiableSet(CollUtil.newHashSet("docx", "pptx", "xlsx", "pdf", "doc", "ppt", "xls"));

    private String lang = "zh_CN";

    private String pdf = "pdf";
    //用户编码
    private String uid = "1";
    @Autowired
    private ConvertService convertService;
    @Autowired
    private DocumentManager documentManager;
    @Autowired
    private MyFileUtil myFileUtil;
    @Autowired
    private OfficeFileServices officeFileServices;
    @Autowired
    private RequestManager requestManager;
    @Autowired
    private FileStorageMutator storageMutator;
    @Autowired
    private FileStoragePathBuilder storagePathBuilder;

    @Autowired
    @Qualifier("urlConvertEditCache")
    private Cache<String, OfficeFile> urlConvertEditCache;
    @Autowired
    private UserMetadataUtil userMetadataUtil;
    @Autowired
    private WopiLocalCacheService wopiLocalCacheService;
    @Autowired
    private OfficePdfServices officePdfServices;
    @Autowired
    private Cache<String, OfficePdf> urlCovertPdfCache;
    private String pdfUrlPrefix;
    @PostConstruct
    public void init() {
        //外网使用
         pdfUrlPrefix = appConfig.getMyAppServiceUrl() + "/downloadpdf?ct=0&fileId=";
    }
    public OfficeFileResultVo useOnlyOfficeByDownload(String url) throws Exception {
        String fileId = CommonUtil.generateMessageDigest(url);
        String fileName = CommonUtil.getFileName(url);
        if (fileName != null) {
            fileName = fileName.replace("+", "_");
        }
        log.info("url={},fileName={}",url,fileName);
        String extension = MyFileUtil.getExtension(fileName);
        log.info("extension={}",extension);
        if (!allowExtensions.contains(extension)) {
            throw OfficeException.valueOf("不支持此种格式。");
        }
        OfficeFileResultVo result = new OfficeFileResultVo();
        log.info("fileId:{},fileName:{}", fileId, fileName);
        OfficeFile officeFile = officeFileServices.getById(fileId);
        boolean isExistInfo = true;
        //如果文件不存在，则进行插入
        if (officeFile == null) {
            //先保存下载文件的地址。
            officeFile = new OfficeFile();
            officeFile.newDownload(fileId, url);
            officeFile.setTitle(fileName);
            officeFile.setSourceName(fileName);
            //定义文件名
            officeFile.setName(fileId + "." + extension);
            officeFile.setExt(extension);
            officeFileServices.save(officeFile);
            userMetadataUtil.createUserMetadata(uid, officeFile);
            isExistInfo = false;
        }
        // 需要转码，并支持转码，速度估计有点慢，后期看看如何优化
        result.setOfficeFile(officeFile);
        if (convertExtensions.contains(extension) && documentManager.getDefaultConvertExtension(fileName) != null) {
            OfficeFile tmp = urlConvertEditCache.getIfPresent(fileId);
            // 如果正在转码，则返回错误信息
            if (tmp != null) {
                throw OfficeConverterRunningException.valueOf("转ooxml该文件正在转码进行中，请稍后重试访问。");
            }
            //即使需要转码，如果数据库中已经存在，则直接返回
            if (isExistInfo) {
                return result;
            }
            //开始转码逻辑
            urlConvertEditCache.put(fileId, officeFile);
            try {
                // 获取文件密码（如果存在）
                String filePass = null;
                // 获取目标转换格式扩展名，若未提供则默认为 ooxml
                String conversionExtension = "ooxml";
                // 构建转换请求参数
                ConvertRequest convertRequest = ConvertRequest.builder()
                        .password(filePass)
                        .outputtype(conversionExtension)
                        .region(lang)
                        .async(false)
                        .title(officeFile.getTitle())
                        .build();
                log.info("convertRequest={}", convertRequest);
                // 执行文件转换操作
                ConvertResponse convertResponse = convertService.processConvert(convertRequest, fileId);
                // 若转换过程中出现错误或未返回有效文件链接，则直接返回响应内容
                if (convertResponse.getError() != null || convertResponse.getFileUrl() == null) {
                    log.error("convertResponse={}", convertResponse);
                    throw OfficeException.valueOf("存在转码错误");
                }
                // 获取转换后文件的URL和类型
                String newFileUri = convertResponse.getFileUrl();
                String newFileType = convertResponse.getFileType();
                log.info("newFileUri={},newFileType={}", newFileUri, newFileType);
                // 验证转换后的文件类型是否在支持的格式列表中
                if (!new FormatsList(documentManager.getFormats()).getFormats().stream().anyMatch(
                        f -> newFileType.equals(f.getName()) && f.getType() != null)
                ) {
                    throw OfficeException.valueOf("不支持此种格式。");
                }
                //String nameWithInternalExt = documentManager.getBaseName(officeFile.getName()) + "." + newFileType;
                //String correctedName = documentManager.getCorrectName(officeFile, nameWithInternalExt);
                //转码后的文件路信息，还是存在远端到路径上，此处，暂不执行下载逻辑
                //officeFile.setName(correctedName);
                officeFile.setNewExt(newFileType);
                officeFile.setDownloadUrl(newFileUri);
                //存在本地
                officeFile.setLocalFlag(OfficeFile.LOCAL_FLAG_NO);
                officeFileServices.save(officeFile);
                // 创建用户对转换后文件的元数据记录
                userMetadataUtil.createUserMetadata(uid, officeFile);
                //转码文件下载
                downloadToLocalPath(officeFile);
            } finally {
                //执行完毕删除缓存
                urlConvertEditCache.invalidate(fileId);
            }
        } else {
            //如果是首次访问，则下载文件为了下载请求
            if (!isExistInfo) {
                downloadToLocalPath(officeFile);
            }
        }
        return result;
    }

    private void downloadToLocalPath(OfficeFile officeFile) {
        String fileLocation = storagePathBuilder.getFileLocation(officeFile, officeFile.getName());
        File file = new File(fileLocation);
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        FileItem fileItem = new FileItem();
        fileItem.setFileId(officeFile.getId());
        fileItem.setUrl(officeFile.getDownloadUrl());
        fileItem.setLocalPath(fileLocation);
        fileItem.setDataType(FileItem.DATATYPE_OFFICEFILE);
        wopiLocalCacheService.downloadLocalCache(fileItem);
    }


    /**
     * 把officeFile转成pdf
     * @param officeFile
     * @return
     */
    public FilePdfVo converterPdf(OfficeFile officeFile, String url) throws Exception {
        OfficePdf officePdf = officePdfServices.getById(officeFile.getId());
        if (officePdf != null) {
            if (OfficePdf.LOCAL_FLAG_YES.equals(officePdf.getLocalFlag())
                    ||
                (OfficePdf.LOCAL_FLAG_NO.equals(officePdf.getLocalFlag()) && StrUtil.isNotBlank(officePdf.getTmpPdfUrl()))) {
                String pdfUrl = String.format(pdfUrlPrefix + "%s", officePdf.getId());
                return FilePdfVo.create(officePdf.getId(), pdfUrl,officePdf);
            }
            return getFilePdfVoByConverter(officeFile, url);
        } else {
            return getFilePdfVoByConverter(officeFile, url);
        }
    }
    private FilePdfVo getFilePdfVoByConverter(OfficeFile officeFile, String url) throws Exception {
        //开始首次处理
        try {
            //暂时这样处理吧，先这样处理 ,等5s还是没有转码成功报错
            int trynum=10;
            while (trynum>0){
                OfficePdf ifPresent = urlCovertPdfCache.getIfPresent(url);
                if (ifPresent != null) {
                    TimeUnit.MILLISECONDS.sleep(500L);
                }
                trynum--;
            }
            OfficePdf ifPresent = urlCovertPdfCache.getIfPresent(url);
            if (ifPresent != null) {
                log.error("url={},ifPresent={}", url,ifPresent);
                throw OfficeConverterRunningException.valueOf("转pdf提示，该文件正在转码进行中，请稍后重试访问。");
            }
            //开始转码
            String fileName = CommonUtil.getFileName(url);
            if (fileName != null) {
                fileName = fileName.replace("+", "_");
            }
            String extension = MyFileUtil.getExtension(fileName);
            if (!allowExtensions.contains(extension)) {
                throw OfficeException.valueOf("不支持此种格式。");
            }
            String title = documentManager.getBaseName(fileName) + "." + pdf;
            OfficePdf  officePdf = new OfficePdf();
            String id = officeFile.getId();
            officePdf.setId(id);
            officePdf.setLocalFlag(OfficePdf.LOCAL_FLAG_NO);
            officePdf.setTitle(title);
            String name = officePdf.getId() + "." + pdf;
            officePdf.setName(name);
            officePdf.setPath(pdf + "/" + officeFile.getPath());
            officePdf.setCreateTime(LocalDateTime.now());
            officePdf.setUrl(url);
            urlCovertPdfCache.put(url, officePdf);
            ConvertRequest convertRequest = ConvertRequest.builder()
                    .password(null)
                    .outputtype(pdf)
                    .region(lang)
                    .async(false)
                    .title(officeFile.getTitle())
                    .build();
            log.info(" 执行文件转换操作1");
            /**
             * rg.apache.hc.client5.http.ClientProtocolException: ONLYOFFICE URL: http://127.0.0.1/converter?shardkey=b3f59489d344949d94aa2f4187047b98c75d1294529ebe09a081d806f5e90846-1754651443561 return unexpected response.
             * Request: POST /converter?shardkey=b3f59489d344949d94aa2f4187047b98c75d1294529ebe09a081d806f5e90846-1754651443561
             * Response: 504 Gateway Time-out HTTP/1.1
             */
            ConvertResponse convertResponse = convertService.processConvert(convertRequest, officeFile.getId());
                // 若转换过程中出现错误或未返回有效文件链接，则直接返回响应内容
            if (convertResponse.getError() != null || convertResponse.getFileUrl() == null) {
                    log.error("convertResponse={}", convertResponse);
                    throw OfficeException.valueOf("存在转码错误，稍后重试");
            }
            // 获取转换后文件的URL和类型
            String newFileUri = convertResponse.getFileUrl();
            log.info("newFileUri={}",newFileUri);
            officePdf.setTmpPdfUrl(newFileUri);
            officePdf.setUpdateTime(LocalDateTime.now());
            officePdfServices.save(officePdf);
            downloadPdfToLocalPath(officePdf);
            return FilePdfVo.create(officePdf.getId(), newFileUri,officePdf);
        } finally {
            urlCovertPdfCache.invalidate(url);
        }
    }

    private void downloadPdfToLocalPath(OfficePdf officePdf) {
        String fileLocation = storagePathBuilder.getFileLocationPdf(officePdf, officePdf.getName());
        File file = new File(fileLocation);
        File parentFile = file.getParentFile();
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        FileItem fileItem = new FileItem();
        fileItem.setFileId(officePdf.getId());
        fileItem.setUrl(officePdf.getTmpPdfUrl());
        fileItem.setLocalPath(fileLocation);
        fileItem.setDataType(FileItem.DATATYPE_OFFICEPDF);
        wopiLocalCacheService.downloadLocalCache(fileItem);
    }

    /**
     * 返回文件下載地址
     * @param ct
     * @param officePdf
     * @return
     * @throws MalformedURLException
     */
    public  ResponseEntity<Resource> getResourceResponseEntity(int ct, OfficePdf officePdf) throws MalformedURLException {
        Resource resource = null;// load the specified file as a resource
        //本地的地址
        if (OfficeFile.LOCAL_FLAG_YES.equals(officePdf.getLocalFlag())) {
            resource = storageMutator.loadFileAsResourcePdf(officePdf, officePdf.getName());  // load the specified file as a resource
        } else {
            resource = new UrlResource(officePdf.getTmpPdfUrl());
        }
        // create a response with the content type, header and body with the file data
        ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.ok();
        if(ct ==1){
            String contentType = "application/octet-stream";
            //下载文件名称使用title
            String encodedFileName = CommonUtil.encodeFileName(officePdf.getTitle());
            bodyBuilder.contentType(MediaType.parseMediaType(contentType));
            bodyBuilder.header(HttpHeaders.CONTENT_DISPOSITION,
                    "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName)  ;
        }else{
            bodyBuilder.contentType(MediaType.parseMediaType("application/pdf"));
        }
        return bodyBuilder.body(resource);
    }

}
